package rexsee.ebook;

import java.util.ArrayList;

import rexsee.core.style.RexseeDrawable;
import rexsee.core.style.StyleSheet;
import rexsee.core.utilities.Utilities;
import rexsee.ebook.DrawableLine.DrawableGetter;
import android.graphics.Bitmap;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.DisplayMetrics;

public class PageFactory {

	public static final String DEFAULT_PARAGRAPH_SPLIT = "\n";

	public final StyleSheet mStyle;

	private final PageView mPageView;
	private final Rect defaultRect;
	private final String defaultVerticalAlign;
	private final Drawable defaultBackground;
	private final Paint defaultPaint;
	private final ArrayList<Page> mPages;

	private String mData;
	private FactoryThread mThread;
	private boolean isLoading = true;
	public final int mWidth;
	public final int mHeight;

	private final DrawableGetter mGetter;
	private final String mCachePath;

	public PageFactory(PageView pageView, StyleSheet styleSheet, String cachePath, DrawableGetter getter, Drawable background) {

		mPageView = pageView;
		mPages = new ArrayList<Page>();
		mStyle = styleSheet;
		mCachePath = cachePath;
		mGetter = getter;

		DisplayMetrics displayMetrics = pageView.getContext().getResources().getDisplayMetrics();
		float screenDensity = displayMetrics.density;

		int w = mPageView.getWidth();
		int h = mPageView.getHeight();
		mWidth = (w > 0) ? w : Math.round(Utilities.getInt(mStyle.width.toLowerCase().replaceAll("px", ""), 240) * screenDensity);
		mHeight = (h > 0) ? h : Math.round(Utilities.getInt(mStyle.height.toLowerCase().replaceAll("px", ""), 320) * screenDensity);

		defaultRect = new Rect(mStyle.getLabelPaddingLeft(), mStyle.getLabelPaddingTop(), mWidth - mStyle.getLabelPaddingRight(), mHeight - mStyle.getLabelPaddingBottom());
		defaultVerticalAlign = mStyle.text_vertical_align;
		defaultBackground = background;
		defaultBackground.setBounds(0, 0, mWidth, mHeight);
		defaultBackground.setAlpha(Math.round(mStyle.getAlpha() * 255));
		defaultPaint = getPaint(mStyle);

	}

	public void parse(String data) {
		mData = data;
		try {
			mThread = new FactoryThread();
			mThread.start();
		} catch (Exception e) {
			mPageView.error("PageFactory.thread", e.getLocalizedMessage());
		}
	}

	public void clear() {
		mPages.clear();
		mThread = null;
		mData = null;
		isLoading = true;
	}
	public boolean isLoading() {
		return isLoading;
	}
	public Bitmap getLoadingBitmap(String text) {
		try {
			Paint paint = getPaint(mStyle.clone().parseStyle("text-align:center;font-style:italic;"));
			Page loadingPage = new Page(this, "middle", defaultRect, defaultBackground, null, null);
			loadingPage.add(new TextLine(text, 14, paint));
			return loadingPage.draw();
		} catch (Exception e) {
			mPageView.error("PageFactory.getLoadingBitmap", e.getLocalizedMessage());
			return null;
		}
	}

	public void destroy() {
		mThread = null;
	}

	public Bitmap getPage(int index) {
		if (index < 0) index = 0;
		if (isLoading) {
			if (index > mPages.size() - 1) {
				return null;
			} else {
				try {
					return mPages.get(index).draw();
				} catch (Exception e) {
					mPageView.error("PageFactory.getPage", e.getLocalizedMessage());
					return null;
				}
			}
		} else {
			if (index > mPages.size() - 1) index = mPages.size() - 1;
			try {
				return mPages.get(index).draw();
			} catch (Exception e) {
				mPageView.error("PageFactory.getPage", e.getLocalizedMessage());
				return null;
			}
		}
	}

	public int getTotal() {
		return (isLoading) ? -1 : mPages.size();
	}

	public ArrayList<Page> getPages() {
		return mPages;
	}
	public ArrayList<Page> searchText(String text) {
		ArrayList<Page> rtn = new ArrayList<Page>();
		for (int i = 0; i < mPages.size(); i++) {
			Page page = mPages.get(i);
			if (page.containsText(text)) rtn.add(page);
		}
		return rtn;
	}

	public static Paint getPaint(StyleSheet style) {

		Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
		paint.setColor(RexseeDrawable.getIntColor(style.color));
		paint.setTextSize(Utilities.getInt(style.font_size.toLowerCase().replaceAll("px", ""), (int) (style.scale(24) / 1.5)));
		paint.setFakeBoldText(style.font_weight.equals("bold"));
		paint.setTextSkewX((style.font_style.equals("italic")) ? -0.25f : 0f);
		paint.setAlpha((int) (style.getAlpha() * 255));
		float radius = Utilities.getFloat(style.text_shadow_radius, 0);
		float dx = Utilities.getFloat(style.text_shadow_dx, 0);
		float dy = Utilities.getFloat(style.text_shadow_dy, 0);
		int color = RexseeDrawable.getIntColor(style.text_shadow_color);
		paint.setShadowLayer(radius, dx, dy, color);
		if (style.text_align.equalsIgnoreCase("right")) {
			paint.setTextAlign(Align.RIGHT);
		} else if (style.text_align.equalsIgnoreCase("center")) {
			paint.setTextAlign(Align.CENTER);
		} else {
			paint.setTextAlign(Align.LEFT);
		}
		if (style.text_decoration.equalsIgnoreCase("underline")) {
			paint.setUnderlineText(true);
		} else if (style.text_decoration.equalsIgnoreCase("strikethru") || style.text_decoration.equalsIgnoreCase("strikethrough")) {
			paint.setStrikeThruText(true);
		}

		return paint;
	}

	private class FactoryThread extends Thread {

		private final int PARAGRAPH_TYPE_NORMAL = 0;
		private final int PARAGRAPH_TYPE_PAGEBREAK = 1;
		private final int PARAGRAPH_TYPE_PARAGRAPHBREAK = 2;
		private final int PARAGRAPH_TYPE_IMAGE = 3;

		private Page page;
		private String data;

		private Rect currentRect = defaultRect;
		private Paint currentPaint = defaultPaint;
		private int currentLineHeight = (int) (Utilities.getInt(mStyle.line_height.toLowerCase().replaceAll("%", ""), 150) * defaultPaint.getTextSize() / 100);

		public FactoryThread() {
			data = mData;
			int fontSize = Utilities.getInt(mStyle.font_size, 20);
			fontSize = fontSize - 4;
			if (fontSize > 28) fontSize = 28;
			StyleSheet headerStyle = mStyle.clone().parseStyle("text-align:left;horizontal-line:true;font-size:" + fontSize + ";");
			StyleSheet footerStyle = mStyle.clone().parseStyle("text-align:center;horizontal-line:true;font-size:" + fontSize + ";font-style:italic;label:pagenumber;");
			mStyle.label = "";
			page = new Page(PageFactory.this, defaultVerticalAlign, defaultRect, defaultBackground, headerStyle, footerStyle);
		}

		@Override
		public void run() {
			try {
				String paragraph = getParagraph();
				while (paragraph != null) {
					switch (getParagraphType(paragraph)) {
						case PARAGRAPH_TYPE_PAGEBREAK :
							parsePageBreak(paragraph);
							break;
						case PARAGRAPH_TYPE_PARAGRAPHBREAK :
							parseParagraphBreak(paragraph);
							break;
						case PARAGRAPH_TYPE_IMAGE :
							parseImage(paragraph);
							break;
						default :
							parseText(paragraph);
							break;
					}
					paragraph = getParagraph();
				}
				addPage();
				isLoading = false;
			} catch (Exception e) {
				mPageView.error("PageFactory.createFactory", e.getLocalizedMessage());
			}
		}

		private void parsePageBreak(String pageBreak) {
			try {
				String styleString = Utilities.getAttribute(pageBreak, "style");
				StyleSheet style = mStyle.clone().parseStyle(styleString);
				Drawable background;
				if ((style.label_background_color.equalsIgnoreCase(mStyle.label_background_color)) && (style.getAlpha() == mStyle.getAlpha())) {
					background = defaultBackground;
				} else {
					background = (mGetter == null) ? RexseeDrawable.getDrawable(mPageView.getContext(), null, style.label_background_color) : mGetter.run(style.label_background_color);
					background.setBounds(0, 0, mWidth, mHeight);
					background.setAlpha(Math.round(style.getAlpha() * 255));
				}
				currentRect = new Rect(style.getLabelPaddingLeft(), style.getLabelPaddingTop(), mWidth - style.getLabelPaddingRight(), mHeight - style.getLabelPaddingBottom());

				String headerString = Utilities.getAttribute(pageBreak, "header");
				StyleSheet header = mStyle.clone().parseStyle(headerString);

				String footerString = Utilities.getAttribute(pageBreak, "footer");
				StyleSheet footer = mStyle.clone().parseStyle(footerString);

				addLine(new PageBreakLine(currentRect, style.text_vertical_align, background, header, footer));
			} catch (Exception e) {
				mPageView.error("PageFactory.parsePageBreak", e.getLocalizedMessage());
			}
		}
		private void parseParagraphBreak(String paragraphBreak) {
			try {
				String styleString = Utilities.getAttribute(paragraphBreak, "style");
				StyleSheet style = mStyle.clone().parseStyle(styleString);
				Paint p = getPaint(style);
				currentPaint = (p.equals(defaultPaint)) ? defaultPaint : ((p.equals(currentPaint) ? currentPaint : p));
				currentLineHeight = (int) (Utilities.getInt(style.line_height.toLowerCase().replaceAll("%", ""), 150) * currentPaint.getTextSize() / 100);
			} catch (Exception e) {
				mPageView.error("PageFactory.parseParagraphBreak", e.getLocalizedMessage());
			}
		}
		private void parseImage(String image) {
			try {
				String url = Utilities.getAttribute(image, "src");
				String w = Utilities.getAttribute(image, "width");
				int width = (w == null) ? -1 : Utilities.getInt(w.replaceAll("px", ""), -1);
				String h = Utilities.getAttribute(image, "height");
				int height = (h == null) ? -1 : Utilities.getInt(h.replaceAll("px", ""), -1);
				addLine(new DrawableLine(mPageView.getContext(), url, width, height, currentRect.width(), currentRect.height(), mCachePath, mGetter));
			} catch (Exception e) {
				mPageView.error("PageFactory.parseImage", e.getLocalizedMessage());
			}
		}
		private void parseText(String text) {
			try {
				if (text.length() == 0) {
					addLine(new TextLine("", currentLineHeight, currentPaint));
				}
				String title = null;
				if (text.trim().toLowerCase().startsWith("<title>")) {
					title = text.split(">")[1];
					if (title != null) title = title.split("<")[0];
					text = text.replace("<title>", "");
					text = text.replace("</title>", "");
					text = text.replace("<TITLE>", "");
					text = text.replace("</TITLE>", "");
				}
				while (text.length() > 0) {
					int size = currentPaint.breakText(text, true, currentRect.width(), null);
					TextLine line = new TextLine(text.substring(0, size), currentLineHeight, currentPaint);
					if (title != null) {
						line.indexTitle = title;
						title = null;
					}
					addLine(line);
					text = text.substring(size);
				}
			} catch (Exception e) {
				mPageView.error("PageFactory.parseText", e.getLocalizedMessage());
			}
		}

		private int getParagraphType(String paragraph) {
			String check = paragraph.trim().toLowerCase();
			if (check.startsWith("<pagebreak ") || check.startsWith("<pagebreak>") || check.startsWith("</pagebreak>") || check.startsWith("<pagebreak/>")) return PARAGRAPH_TYPE_PAGEBREAK;
			else if (check.startsWith("<paragraphbreak ") || check.startsWith("<paragraphbreak>") || check.startsWith("</paragraphbreak>") || check.startsWith("<paragraphbreak/>")) return PARAGRAPH_TYPE_PARAGRAPHBREAK;
			else if (check.startsWith("<img ")) return PARAGRAPH_TYPE_IMAGE;
			else return PARAGRAPH_TYPE_NORMAL;
		}
		private String getParagraph() {
			if (data == null || data.length() == 0) return null;
			String paragraph;
			int index = data.indexOf(DEFAULT_PARAGRAPH_SPLIT);
			if (index >= 0) {
				paragraph = data.substring(0, index);
				data = data.substring(index + 1);
			} else {
				paragraph = data;
				data = null;
			}
			paragraph = paragraph.replaceAll("\\t", "        ");
			return paragraph;
		}

		private void addLine(LineInterface newLine) {
			try {
				if (newLine.isPageBreak()) {
					addPage();
					String inheriteTitle = page.inheriteTitle;
					page = new Page(PageFactory.this, (PageBreakLine) newLine);
					page.inheriteTitle = inheriteTitle;
				} else {
					if (!page.canAdd(newLine)) {
						addPage();
						page = page.clone();
					}
					page.add(newLine);
					String title = newLine.getIndexTitle();
					if (title != null) {
						page.indexTitles.add(title);
						page.inheriteTitle = title;
					} else {

					}
				}
			} catch (Exception e) {
				mPageView.error("PageFactory.addLine", e.getLocalizedMessage());
			}
		}
		private void addPage() {
			try {
				if (page.getContentHeight() == 0) return;
				page.index = mPages.size();
				mPages.add(page);
				mPageView.reload();
			} catch (Exception e) {
				mPageView.error("PageFactory.addPage", e.getLocalizedMessage());
			}
		}

	}

}
